home *** CD-ROM | disk | FTP | other *** search
/ PCMania 64 / PCMania CD64_1.iso / phy / phy005 / files / articulo.t10 < prev    next >
Encoding:
Text File  |  1997-09-19  |  19.2 KB  |  1 lines

  1.                                                                                      ε                  Administración de la memoria bajo DOS (I)                                                                                                                En este artículo se pretende mostrar las distintas formas de administrar la        memoria en el sistema operativo DOS ayudandonos de rutinas de ejemplo y pequeños     programas. Se ha dividido en 2 el artículo, explicando en el primero los             conceptos básicos, la memoria convencional y la XMS y dejando para el segundo        (para el próximo número de la Phy) la memoria EMS, el modo flat y una pequeña        introducción al modo protegido. El nivel de los artículos será medio ya que          hará falta conocer el lenguaje ensamblador y dominar los conceptos de                interrupciones y segmentos.                                                                                                                                               φ       La memoria convencional                                                                                                                                             La llamada memoria convencional o memoria baja es una zona de memoria más          bien escasa en estos dias donde, bajo DOS, se cargan los programas y se les          asignan recursos. Mide exactamente 640Kb (cifra odiada por muchos) y se              divide en segmentos de 64Kb con una granularidad de 16 bytes (para saber más         sobre esto, ver sección de programación en ensamblador).                               Para poder pedir memoria dinámicamente al DOS en ensamblador, lo primero           que hay que hacer es liberar la memoria que el DOS nos ha asignado y que no          vamos a utilizar. El sistema operativo, al cargar todo programa COM o EXE,           le da toda la memoria libre que resta hasta la zona ocupada por el própio            sistema y la BIOS (segmento 0A000h) con lo que cualquier petición de memoria         que le realizemos (a no ser que sea muy pequeña) nos devolverá un precioso           error de memoria insuficiente.                                                         Bien, ya sabemos que tenemos que liberar la memoria no ocupada, pero ¿como         saber que memoria es la que estoy ocupando? Sencillo, el tamaño estrictamente        necesario es el que mide el PSP (siempre 256 bytes), el del código y el de la        pila. Si recordamos todo eso de la disposición de la pila, el segmento de            código y el PSP, pues podemos escribir una función para liberar la memoria           en un fichero EXE o COM muy facilmente. Como ejemplo aquí está la que libera         la memoria de un EXE, luego esplicaré que hace la función δ4Ahπ de la INT 21h:                                                                                            ΓLiberaMEM       PROC                   Ω ;  Función que libera la memoria           Γ                MOV     BX, SS         Ω ; reservada por el DOS a nuestro           Γ                MOV     AX, ES         Ω ; programa y que no es utilizada.          Γ                SUB     BX, AX         Ω ;  ES debe contener el PSP.                Γ                MOV     AX, SP                                                      Γ                ADD     AX, 15d                                                     Γ                SHR     AX, 4                                                       Γ                ADD     BX, AX                                                      Γ                MOV     AH, 4Ah                                                     Γ                INT     21h                                                         Γ                RET                                                                 ΓLiberaMEM       ENDP                                                                                                                                                       En los lenguajes de alto nivel como el C o Pascal, la cosa cambia; a priori        no sabemos si el runtime del compilador ha liberado esta memoria o no ya que         esto depende del fabricante del lenguaje o, más aún, de las funciones que se         vayan a usar durante la ejecución del programa. Teniendo esto en cuenta y            siempre que nos sea posible, se deberia dejar al propio lenguaje que adminis-        trara la memoria y pedirsele mediante las llamadas estandar y no desde el            assembler, aunque siempre podemos arriesgarnos o debuggear el runtime para ver       lo que hace. En concreto, por ejemplo, el Turbo Pascal 6.0, no libera la memo-       ria y la usa como le parece concediendonos recursos cuando se los pedimos y li-      berandolos automáticamente al finalizar.                                               El API del DOS se proviene de unas cuantas funciones para la administración        de la memoria entre las que destacan la δ48hπ ("allocate memory"), la δ49hπ          ("free memory") y la δ4Ahπ ("modify memory block"). La estructura interna que        usa el DOS para hacer estas operaciones, queda fuera del alcance de este             artículo, pero si alguien está interesado, puede buscar en libros especializa-       dos como por ejemplo el "φPC Internoπ", documentos o FAQs sobre los MCBs ("memory    control block") que es el nombre de estas estructuras (otra buena fuente de          información sobre esto son los virus, sobre los que precisamente hay un par de       articulo en cada número de la Phy).                                                                                                                                       Ω  Interrupción:  21h      Función:  48h          "Allocate memory"                  Σ  Parámetros:    AH = 48h                                                           Σ                 BX = Párrafos que se necesita                                      Σ  Devuelve:      Si carry ->  AX = Código de error                                  Σ                              BX = Tamaño máximo permitido                          Σ                 Sino ->  AX = Segmento disponible.                                                                                                                        A esta función se le debe pasar en BX el número de párrafos de memoria que         se necesitan y devuelve un segmento donde a partir de la dirección 0000, se          puede usar libremente sin miedo (teoricamente) a que te lo pisen o a cargarte        el sistema. Los párrafos de memoria son una medida de cantidad que equivale a        16 bytes, por lo que si necesitamos 60000 bytes, hemos de pedir 10000 párrafos,      sencillo, ¿no? Es importante que en el segmento que nos concederá el sistema,        no escribamos más allá de lo que estrictamente hemos pedido o nos cargaremos         el MCB siguiente con lo que el sistema se colgará. El código de error que nos        devuelve es 7 o 8, el primero significa que algun MCB está destruido y el otro       que no queda memoria libre.                                                                                                                                               Ω  Interrupción:  21h      Función:  49h          "Free allocated memory"            Σ  Parámetros:    AH = 49h                                                           Σ                 ES = Segmento reservado a liberar                                  Σ  Devuelve:      Si carry ->  AX = Código de error                                                                                                                         Esta función, simplemente devuelve la memoria que habiamos pedido con la           interrupción anterior. En ES debemos poner lo que habia devuelto AX antes y          si el carry está a 1 en AX podremos tener un código de error 7 o 9, que significan   que los MCBs estan destruidos o que el segmento no es válido respectivamente.                                                                                             Ω  Interrupción:  21h      Función:  4Ah          "Modify memory allocation"         Σ  Parámetros:    AH = 4Ah                                                           Σ                 BX = Párrafos que se necesita                                      Σ                 ES = Segmento a modificar                                          Σ  Devuelve:      Si carry ->  AX = Código de error                                  Σ                              BX = Tamaño máximo permitido                                                                                                                 Con esta función podemos modificar el tamaño de un determinado bloque de           memoria asignado como hemos hecho, por ejemplo para la función que liberaba          la memoria no usada por el programa. En BX pasamos el número de párrafos             que queremos y en ES el segmento que nos devolvieron en una llamada anterior         (en el caso de liberar la memoria asignada por el DOS, el segmento del PSP           que es el primero que nos dan). Los códigos de error que nos devuelve son            7, 8 o 9 que significan respectivamente MCBs destruidos, memoria insuficiente        y segmento no válido.                                                                  Para ilustrar esta forma de administrar la memoria teneis en el directorio         'φMEMORYπ' un par de programas llamados 'memcon1.asm' y 'memcon2.asm' que mues-      tran lo que se puede y debe hacer y lo que no. El primero pide memoria y luego       la devuelve antes de salir sin problemas; el segundo pide memoria y escribe en       ella más bytes de los pedidos con lo que al tratar de volver shell del sistema,      hay muchas posibilidades de no poder hacerlo.                                                                                                                             φ       La memoria XMS                                                                                                                                                      Se le llama así a la memoria que reside por encima de 1Mb y que en el diseño       original del 8086 no exisitia. Como los compatibles IBM fueron evolucionando         y las aplicaciones comenzaron a pedir más y más recursos, al 80286 se le             proporcionó un bus de direcciones un poco más grande con lo que se le dotó de        hasta 16Mb direccionables. Con ello surgia un problema de compatibilidad que         impedia que los 15 nuevos megas se manejaran como antes se hacia con la memoria      convencional y ello hizo aparecer en escena el estandar EMS de la mano de            IBM, Lotus y Microsoft (tm, todos). Pero esto no nos interesa en este número         almenos, lo importante es que la EMS entraba al ordenador mediante hardware          y eso no era una gran idea y por ello se evolucionó con la llegada de los 80386      y el modo protegido (mejor que el del 80286) hacia un estandar llamado XMS           que entraba mediante un driver con lo cual se hacia más cómodo.                        El driver del que hablo es el 'EMM386.EXE' que seguro tienes instalado en          tu PC ya que tambien se encarga de simular memoria EMS y por tanto es casi           esencial en todos los programas que usen más de medio mega de memoria. Su            funcionamiento interno lo "intuiremos" en el próximo número cuando hablemos del      modo protegido y el modo flat ya que no dista mucho de lo que haremos allí.            Para usar la memoria XMS lo primero que hay que hacer es comprobar que el          driver 'EMM386.EXE' ha sido cargado correctamente. Para ello, tenemos una            función del multiplexor (INT 2Fh) que nos devolverá información sobre si está        o no instalado y sobre la dirección del driver en caso afirmativo. Para saber        si está en memoria el driver llamaremos a la INT 2Fh con el valor 4300h en AX        y si al regresar, en AL tenemos el valor 80h es que el driver se encuentra en        memoria y por tanto podemos preguntar por su dirección. Para hacer esto último,      llamaremos con AX=4310h a la INT 2Fh y nos devolverá en ES:BX la dirección don-      de se encuentra el driver instalado.                                                   A partir de este momento, nos olvidamos de las interrupciones y todo lo que        haremos será llamar al driver (con CALL FAR) dejándole el número de función          que queremos que haga en AH como si fuera una interrupción cualquiera. La lista      de las funciones del driver es esta:                                                                                                                                      Ω          00 --> Γ "Get XMS version number"                                         Ω          01 --> Γ "Request High Memory Area"                                       Ω          02 --> Γ "Release High Memory Area"                                       Ω          03 --> Γ "Global enable A20, for using the HMA"                           Ω          04 --> Γ "Global disable A20"                                             Ω          05 --> Γ "Local enable A20"                                               Ω          06 --> Γ "Local disable A20"                                              Ω          07 --> Γ "Query A20 state"                                                Ω          08 --> Γ "Query free extended memory"                                     Ω          09 --> Γ "Allocate extended memory block"                                 Ω          0A --> Γ "Free extended memory block"                                     Ω          0B --> Γ "Move extended memory block"                                     Ω          0C --> Γ "Lock extended memory block"                                     Ω          0D --> Γ "Unlock extended memory block"                                   Ω          0E --> Γ "Get handle information"                                         Ω          0F --> Γ "Reallocate extended memory block"                               Ω          10 --> Γ "Request upper memory block"                                     Ω          11 --> Γ "Release upper memory block"                                     Ω          12 --> Γ "Reallocate upper memory block" (v3.0+)                          Ω          88 --> Γ "Query free extended memory" (v3.0+)                             Ω          89 --> Γ "Allocate any extended memory" (v3.0+)                           Ω          8E --> Γ "Get extended EMB handle information" (v3.0+)                    Ω          8F --> Γ "Reallocate any extended memory block" (v3.0+)                                                                                                          En realidad de todas estas funciones usaremos sólo unas pocas como la 09h,         la 0Ah y la 0Bh, ya que todas las demás desde el punto de vista que aquí se          está tratando son prescindibles. De todos modos, es muy interesante saber para       que sirven y como se usan ya que gracias a algunas de ellas se puede acceder         a los UMBs (para instalar residentes), cambiar el estado de la línea A20, etc.         La función 09h se encarga de pedir memoria al driver tal y como lo haciamos        con la 48h de la INT 21h. A esta función le pasaremos en DX el número de Kb          (ojo, 1Kb=1024 bytes) que deseamos y nos devolverá un handle de 16bits en DX         que deberemos guardar. La condición de error nos la dará en AX, si este regis-       tro es 0, hay error y debemos mirar en BL el código de error devuelto (luego         hablaré sobre estos códigos).                                                          La función 0Ah es tan simple como la anterior, se le pasa en DX el handle de       memoria que se quiere liberar y ya está. En AX dirá si hay o no algún error,         si AX vale 0 es que ha ocurrido algo inesperado y en BL se encontrará el código      de error correspondiente.                                                              La cosa se complica un poco con la función 0Bh que es la encargada de mover        bloques de memoria entre RAM convencional y alta. No se le pasan los parámetros      en los registros ni en la pila, sino en una estructura especial llamada EMM          que apuntaremos antes de llamar con DS:SI. Como siempre, si al regresar AX es        0 es que ha ocurrido algun error y en cuyo caso tendremos en BL el código de         error.                                                                                 La estructura EMM se compone de 3 dobles WORDs y 2 WORDs dispuestas de la          siguiente forma:                                                                                                                                                          Γ         Longitud       DD      ?                                                   Γ         SHandle        DW      ?                                                   Γ         SOffset        DD      ?                                                   Γ         DHandle        DW      ?                                                   Γ         DOffset        DD      ?                                                                                                                                        El parámetro 'Logitud' debe contener la longitud en bytes del bloque a copiar        y que dadas las limitaciones del DOS y el modo real no deberia de pasar de           64Kb, aunque hay trucos enrevesados para poder pasar de este tamaño. Los pará-       metros 'SHandle' y 'DHandle' ("source handle" y "destination handle") son los        handles de XMS a usar, es decir, los que devuelve la función 09h. Podrias pen-       sar ahora que entonces no se puede copiar entre memoria convencional y memoria       XMS ya que los handles devueltos por el driver se refieren a la memoria por en-      cima de 1Mb y... pues no es del todo correcto ya que cuando quieras mover blo-       ques de memoria entre la convencional y la XMS, simplemente debes poner como         handle de la convencional el 0. Por último, los parámetros 'SOffset' y               'DOffset' son el offset a partir del cual copiar; en XMS se refiere al despla-       zamiento dentro del hipotético segmento reservado y en memoria baja hay que po-      ner la dirección con segmento y offset hasta la que copiar o desde la cual se        copia.                                                                                 Para ilustrar todo esto he preparado una pequeña libreria de funciones que         te permitiran usar desde el ensamblador toda la memoria XMS. El código fuente        está en el fichero 'δXMSMEM.INCπ' y ha sido comentado a conciencia con lo que        supongo que no habrá problema alguno en entenderlo todo y si es necesario en         hacerle los prototipos para incluirlo en un lenguaje de alto nivel. De todos         modos, si no sabes mucho ensamblador y quieres manejar la memoria XMS desde tu       lenguaje favorito, enviame un mail y para el próximo número incluiré una ver-        sión para el lenguaje que me pidas. Además de esto, en el directorio 'σMEMORYπ'      hay un fichero llamado 'δXMSDRV.TXTπ' que ha sido extraido de la lista de inte-      rrupciones de φRalf Brown (v5.3)π y que detalla todas las funciones del driver       EMM386 i los códigos de error correspondientes.                                                                                                                                                                                       ∞  Navi/PhyMosys